#include <stdio.h>
#include "platform_api.h"
#include "att_db.h"
#include "gap.h"
#include "btstack_event.h"
#include "btstack_defines.h"
#include "gatt_client.h"
#include "FreeRTOS.h"
#include "timers.h"
#include "btstack_sync.h"
#include "ad_parser.h"
#include "bluetooth.h"
#include "bluetooth_hci.h"
#include "btstack_util.h"
#include "btstack_mt.h"
#include "gatt_client.h"
#include "string.h"
#include "User_Led_Ws2812_Drive.h"
// GATT characteristic handles
#include "../data/gatt.const"

const static uint8_t adv_data[] = {
    #include "../data/advertising.adv"
};

#include "../data/advertising.const"

const static uint8_t scan_data[] = {
    #include "../data/scan_response.adv"
};

#include "../data/scan_response.const"

const static uint8_t profile_data[] = {
    #include "../data/gatt.profile"
};

static TimerHandle_t rssi_timer = NULL;

#define USER_MSG_WRITE_CHAR         7
#define USER_MSG_READ_RSSI         8
static gatt_client_notification_t gattClientNotification;

static struct btstack_synced_runner *synced_runner = NULL;

typedef struct sc_peer_addr_s
{
    bd_addr_type_t type;
    bd_addr_t       addr;
}sc_peer_addr_t;

hci_con_handle_t central_handle = 0xffff;
static const bd_addr_t slave_addr = { 0xC1, 0x91, 0x68, 0x2C, 0x00, 0x00 };
static const bd_addr_t rand_addr =  { 0xC0, 0x91, 0x68, 0x2C, 0x00, 0x00 };
static const scan_phy_config_t configs[] =
{
    {
        .phy = PHY_1M,
        .type = SCAN_PASSIVE,
        .interval = 200, // 300
        .window = 150    // 50
    }
};

static  initiating_phy_config_t phy_configs[] =
{
    {
        .phy = PHY_1M,
        .conn_param =
        {
            .scan_int = 200, // 200
            .scan_win = 150, // 80
            .interval_min = 45,
            .interval_max = 45,
            .latency = 0,
            .supervision_timeout = 500,
            .min_ce_len = 4,
            .max_ce_len = 7
        }
    }
};
static void synced_gap_read_rssi(struct btstack_synced_runner *runner, void *user_data);
void write_characteristic_descriptor_callback(uint8_t packet_type, uint16_t _, const uint8_t *packet, uint16_t size);
void write_characteristic_value_callback(uint8_t packet_type, uint16_t _, const uint8_t *packet, uint16_t size);
void ble_scan_enable(uint8_t enable);

static uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset,
                                  uint8_t * buffer, uint16_t buffer_size)
{
    switch (att_handle)
    {

    default:
        return 0;
    }
}

static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode,
                              uint16_t offset, const uint8_t *buffer, uint16_t buffer_size)
{
    switch (att_handle)
    {

    default:
        return 0;
    }
}

static void user_msg_handler(uint32_t msg_id, void *data, uint16_t size)
{
    switch (msg_id)
    {
    case USER_MSG_WRITE_CHAR:
        {
            gatt_client_write_value_of_characteristic(
                write_characteristic_value_callback,
                central_handle,
                HANDLE_BLINQ_WRITE,
                size,
                (uint8_t*)data);
        }
        break;
    case  USER_MSG_READ_RSSI:
        {

          
        } break;
    default:
        break;
    }
}

void ble_ctrl_send_cmd(void *data, uint16_t len)
{
    if(central_handle == 0xffff) return;
	btstack_push_user_msg(USER_MSG_WRITE_CHAR, data, len);
}
static  int8_t int8_rssi = -125;
int8_t ble_ctrl_read_rssi(void)
{
    if(central_handle == 0xffff) return -125;
    int ret = btstack_sync_run(synced_runner, synced_gap_read_rssi,(int8_t *)(NULL));
    return int8_rssi;
}

void user_synced_create_conn(struct btstack_synced_runner *runner, void *userdata)
{
    platform_printf("synced create connection (timeout 5s)...\r\n");
    sc_peer_addr_t *t_peer_addr = (sc_peer_addr_t *)userdata;
    le_meta_event_enh_create_conn_complete_t complete = {0};

    platform_printf("connect interval: %d, %d\r\n", phy_configs[0].conn_param.interval_min, phy_configs[0].conn_param.interval_max);

    int r = gap_sync_ext_create_connection(runner,
                                    INITIATING_ADVERTISER_FROM_PARAM,
                                    BD_ADDR_TYPE_LE_RANDOM,           // Own_Address_Type,
                                    t_peer_addr->type,                // Peer_Address_Type,
                                    t_peer_addr->addr,                // Peer_Address,
                                    sizeof(phy_configs) / sizeof(phy_configs[0]),
                                    phy_configs,
                                    5000,
                                    &complete);
    platform_printf(r == 0 ? "Succeed:%d\n" : "Failed:0x%x\n", r);
    if(r)  ble_scan_enable(1);        
}

void write_characteristic_descriptor_callback(uint8_t packet_type, uint16_t _, const uint8_t *packet, uint16_t size)
{
    switch (packet[0])
    {
        case GATT_EVENT_QUERY_COMPLETE:
          //  platform_printf("write_characteristic_descriptor COMPLETE: %d\n", gatt_event_query_complete_parse(packet)->status);
            if (gatt_event_query_complete_parse(packet)->status != 0)
            {
                platform_printf("===>notify enable error.\n");
                break;
            }
            break;
    }
}

void write_characteristic_value_callback(uint8_t packet_type, uint16_t _, const uint8_t *packet, uint16_t size)
{
    switch (packet[0])
    {
    case GATT_EVENT_QUERY_COMPLETE:
        platform_printf("write_characteristic_value COMPLETE: %d\n", gatt_event_query_complete_parse(packet)->status);
        if (gatt_event_query_complete_parse(packet)->status != 0)
        {
            gap_disconnect(0);
            break;
        }
        break;
    }
}


#include "bsp_msg.h"
extern att_send_t flyData;
static void output_notification_handler(uint8_t packet_type, uint16_t channel, const uint8_t *packet, uint16_t size)
{
    const gatt_event_value_packet_t *value;
    uint16_t value_size;
    switch (packet[0])
    {
    case GATT_EVENT_NOTIFICATION:
        value = gatt_event_notification_parse(packet, size, &value_size);
        if (sizeof(att_send_t) == value_size){
            const att_send_t* pFly = (att_send_t*)value->value;
            memcpy(&flyData,pFly,value_size);        
        }
        break;
    case GATT_EVENT_INDICATION:
        value = gatt_event_indication_parse(packet, size, &value_size);
        if (value_size)
        {
            platform_printf("%%Indication of #%d-%d:",channel,value->handle);
        }
        break;
    }
}

void ble_scan_enable(uint8_t enable){
    uint8_t ret = gap_set_ext_scan_enable(enable, 0, 0, 0);
    if(ret)
    {
        platform_printf("***************Scan operate err***************\r\n");   
    }   
    else{
        show_app_state(APP_ADV);
    }    
}

sc_peer_addr_t t_peer_addr = {.addr = {0,0,0,0,0,0},.type = BD_ADDR_TYPE_LE_RANDOM};

static void setup_scan(void)
{
    uint8_t ret = gap_set_ext_scan_para(BD_ADDR_TYPE_LE_RANDOM, 
                          SCAN_ACCEPT_ALL_EXCEPT_NOT_DIRECTED,
                          sizeof(configs) / sizeof(configs[0]),
                          configs);
    if(ret)
    {
        platform_printf("***************Scan setup err***************\r\n");   
    }                           
}

static void synced_gap_read_rssi(struct btstack_synced_runner *runner, void *user_data)
{
    int err;
    if ((err = gap_sync_read_rssi(runner, central_handle,&int8_rssi)) != 0)
    {
        printf("err = %d:\n",err);
    }
 //   printf("RSSI: %ddBm\n", int8RssiVal);
}


static void user_packet_handler(uint8_t packet_type, uint16_t channel, const uint8_t *packet, uint16_t size)
{

    uint8_t event = hci_event_packet_get_type(packet);
    const btstack_user_msg_t *p_user_msg;
    if (packet_type != HCI_EVENT_PACKET) return;

    switch (event)
    {
    case BTSTACK_EVENT_STATE:
        if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING)
            break;
        synced_runner = btstack_create_sync_runner(1);
        if(!synced_runner)
        {
            platform_printf("HCI:Synced runner create failed\n");
            return;
        }
        platform_printf("HCI:Synced runner create successed\n");  
        gap_set_random_device_address(rand_addr);  
        setup_scan();        
        ble_scan_enable(1);        
       
        break;

    case HCI_EVENT_COMMAND_COMPLETE:
        switch (hci_event_command_complete_get_command_opcode(packet))
        {
        default:
            break;
        }
        break;

    case HCI_EVENT_LE_META:
        switch (hci_event_le_meta_get_subevent_code(packet))
        {
        case HCI_SUBEVENT_LE_EXTENDED_ADVERTISING_REPORT:
        {
                const le_ext_adv_report_t *report = decode_hci_le_meta_event(packet, le_meta_event_ext_adv_report_t)->reports;

                reverse_bd_addr(report->address, t_peer_addr.addr);

                if(0 == memcmp(slave_addr,t_peer_addr.addr, 6))
                {
  
                        ble_scan_enable(0); 
                        platform_printf("HCI:connecting ...\r\n"); 

                        if (report->evt_type & HCI_EXT_ADV_PROP_USE_LEGACY)
                            phy_configs[0].phy = PHY_1M;
                        else
                            phy_configs[0].phy = (phy_type_t)(report->s_phy != 0 ? report->s_phy : report->p_phy);
                        t_peer_addr.type = report->addr_type;
                        uint8_t  ret = btstack_sync_run(synced_runner, user_synced_create_conn, (sc_peer_addr_t*)&t_peer_addr);
                        platform_printf("HCI:btstack_sync_run:%d\r\n",ret); 
                 }
             
        }
            break;                        
        case HCI_SUBEVENT_LE_ENHANCED_CONNECTION_COMPLETE:
        case HCI_SUBEVENT_LE_ENHANCED_CONNECTION_COMPLETE_V2:
        {
            const le_meta_event_enh_create_conn_complete_t * complete =
                decode_hci_le_meta_event(packet, le_meta_event_enh_create_conn_complete_t);
            if (complete->status != 0) break;  
            platform_printf("HCI:ble connected handle:%d\r\n",complete->handle); 
            if (HCI_ROLE_MASTER == complete->role)
            {                   
                gatt_client_is_ready(complete->handle);   
                gatt_client_listen_for_characteristic_value_updates(
                                                                    &gattClientNotification, 
                                                                    output_notification_handler,
                                                                    complete->handle,HANDLE_BLINQ_NOTIFY); 
                central_handle = complete->handle;
                show_app_state(APP_CONN);
                
            }                    
        }
            break;
        default:
            break;
        }

        break;

    case HCI_EVENT_DISCONNECTION_COMPLETE:
    {
        const event_disconn_complete_t *disc_cmpl = decode_hci_event_disconn_complete(packet);
        platform_printf("Discon handle:%d, reson:%d\r\n",disc_cmpl->conn_handle,disc_cmpl->reason);
        central_handle = 0xffff;
        ble_scan_enable(1); 
    }
        break;

    case ATT_EVENT_CAN_SEND_NOW:
        // add your code
        break;

    case BTSTACK_EVENT_USER_MSG:
        p_user_msg = hci_event_packet_get_user_msg(packet);
        user_msg_handler(p_user_msg->msg_id, p_user_msg->data, p_user_msg->len);
        break;

    default:
        break;
    }
}

static btstack_packet_callback_registration_t hci_event_callback_registration;



uint32_t setup_profile(void *data, void *user_data)
{
    platform_printf("setup profile\n");
    // Note: security has not been enabled.
    att_server_init(att_read_callback, att_write_callback);
    hci_event_callback_registration.callback = &user_packet_handler;
    hci_add_event_handler(&hci_event_callback_registration);
    att_server_register_packet_handler(&user_packet_handler);
    gatt_client_register_handler(user_packet_handler);  
   
    return 0;
}

